#ifndef GST_MoMaStructs_h__
#define GST_MoMaStructs_h__


#include <buildspec.h>
#include <config.h>	
#include <Geometry/IGeometry.h>
#include <Client/ClientUtils/Network/NetworkInterfaceStructs.h>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

#include <cstdint>
#include <string>
#include <vector>

namespace GST {
	namespace ClientUtils {
		
		class MoMaPropertyValue;

		struct Owner;
		typedef boost::shared_ptr<Owner> OwnerPtr;
		typedef std::vector<OwnerPtr> OwnerList;
		typedef boost::shared_ptr<OwnerList> OwnerListPtr;
		struct Owner
		{
			enum OwnerType{User,Group};
			typedef long OwnerId;

			OwnerId id;
			OwnerType type;
			std::string name;
			std::string createTime;
			bool operator==(const Owner& other) const
			{
				if(id!=other.id)
					return false;
				if(type!=other.type)
					return false;
				if(name!=other.name)
					return false;
				if(createTime!=other.createTime)
					return false;
				return true;
			};
		};

		  /**
			* A GST User struct (a GST registered user).
			*/
		struct GST_API_EXPORT User
			: public Owner
		{
			User(const Owner::OwnerId oid, const std::string& name, const std::string & createTime = std::string())
				: Owner()
			{
				this->type = Owner::User;
				this->id = oid;
				this->name = name;		 
				this->createTime = createTime;		
			}

			User(const std::string& name, const std::string & createTime = std::string())
				: Owner()
			{
				this->type = Owner::User;
				this->id = -1;
				this->name = name;		 
				this->createTime = createTime;		
			}
		};
		typedef boost::shared_ptr<User> UserPtr;
		typedef boost::ptr_vector<User> UserList;
		typedef boost::shared_ptr<UserList> UserListPtr;

		  /**
			* A GST Group struct (a GST registered group).
			*/
		struct GST_API_EXPORT Group
			: public Owner
		{
			Group(const Owner::OwnerId oid, const std::string& name, const std::string & createTime = std::string())
				: Owner()
			{
				this->type = Owner::Group;
				this->id = oid;
				this->name = name;		 
				this->createTime = createTime;		
			}

			Group(const std::string& name, const std::string & createTime = std::string())
				: Owner()
			{
				this->type = Owner::Group;
				this->id = -1;
				this->name = name;		 
				this->createTime = createTime;		
			}
		};
		typedef boost::ptr_vector<Group> GroupList;
		typedef boost::shared_ptr<GroupList> GroupListPtr;

		struct ColorScale
		{
			typedef long ColorScaleId;

			ColorScaleId id;
			std::string label; 
		};
		typedef boost::shared_ptr<ColorScale> ColorScalePtr;
		typedef std::vector<ColorScalePtr> ColorScaleList;
		typedef boost::shared_ptr<ColorScaleList> ColorScaleListPtr;

		typedef std::vector<ColorScale::ColorScaleId> ColorScaleIdList;
		typedef boost::shared_ptr<ColorScaleIdList> ColorScaleIdListPtr;

		struct ColorValue
		{
			typedef long ColorValueId;

			ColorValueId id;
			ColorScale::ColorScaleId refers;
			std::string label;
			Geometry::IGeometry::Color color;
			Geometry::IGeometry::TransparencyCode alpha;
		};
		typedef boost::shared_ptr<ColorValue> ColorValuePtr;
		typedef std::vector<ColorValuePtr> ColorValueList;
		typedef boost::shared_ptr<ColorValueList> ColorValueListPtr;

		struct Element
		{
			typedef long ElementId;

			ElementId id;
			Owner::OwnerId owner;
			std::string label;
		};
		typedef boost::shared_ptr<Element> ElementPtr;
		typedef std::vector<ElementPtr> ElementList;
		typedef boost::shared_ptr<ElementList> ElementListPtr;

		struct LinkAdjacency
		{
			enum TargetType{FeatureTarget, ElementTarget};
			typedef long LinkId;
			typedef long ReferId;

			LinkId id;
			ReferId targetId;
			TargetType targetType;
			LinkId parent;
			Owner::OwnerId owner;
			int64_t siblingRank;

			LinkAdjacency()
				: id(-1)
				, targetId(-1)
				, targetType()
				, parent(-1)
				, owner(-1)
				, siblingRank(0){};

			LinkAdjacency(const LinkId &_id,
						  const ReferId &_targetId,
						  const TargetType &_targetType,
						  const LinkId &_parent,
						  const Owner::OwnerId &_owner,
						  int64_t _siblingRank)
				: id(_id)
				, targetId(_targetId)
				, targetType(_targetType)
				, parent(_parent)
				, owner(_owner)
				, siblingRank(_siblingRank){};

			virtual ~LinkAdjacency()
			{}
		};
		typedef boost::shared_ptr<LinkAdjacency> LinkAdjacencyPtr;
		typedef std::vector<LinkAdjacencyPtr> LinkAdjacencyList;
		typedef boost::shared_ptr<LinkAdjacencyList> LinkAdjacencyListPtr;

		class AssignableNode
		{
		protected:
			bool assigned;
		public:

			AssignableNode(bool a) : assigned(a) {}
			virtual ~AssignableNode() {}
			virtual bool isAssigned() const { return assigned;}
			virtual void setAssigned(bool a) {assigned = a;}
		};
		typedef boost::shared_ptr<AssignableNode> AssignableNodePtr;
		typedef std::vector<AssignableNodePtr> AssignableNodeList;
		typedef boost::shared_ptr<AssignableNodeList> AssignableNodeListPtr;

		class AssignableElement
			: public AssignableNode
		{
			ElementPtr element;
			ColorScaleIdListPtr colorScaleIds;

		public:
			AssignableElement(ElementPtr e, ColorScaleIdListPtr c, bool assigedFlag) : element(e), colorScaleIds(c), AssignableNode(assigedFlag) {}
			ElementPtr getElement() const { return element; }
			ColorScaleIdListPtr getColorScaleIds() const { return colorScaleIds; }
		};
		typedef boost::shared_ptr<AssignableElement> AssignableElementPtr;
		typedef std::vector<AssignableElementPtr> AssignableElementList;
		typedef boost::shared_ptr<AssignableElementList> AssignableElementListPtr;

		class AssignableFeatureDesc
			: public AssignableNode
		{
			FeatureDescPtr featureDesc;

		public:
			AssignableFeatureDesc(FeatureDescPtr f, bool assigedFlag) : featureDesc(f), AssignableNode(assigedFlag) {}
			FeatureDescPtr getFeatureDesc() const { return featureDesc; }
		};
		typedef boost::shared_ptr<AssignableFeatureDesc> AssignableFeatureDescPtr;
		typedef std::vector<AssignableFeatureDescPtr> AssignableFeatureDescList;
		typedef boost::shared_ptr<AssignableFeatureDescList> AssignableFeatureDescListPtr;

		typedef boost::shared_ptr<MoMaPropertyValue> MoMaPropertyValuePtr;
		typedef std::vector<MoMaPropertyValuePtr> MoMaPropertyValueList;
		typedef boost::shared_ptr<MoMaPropertyValueList> MoMaPropertyValueListPtr;

		struct LinkedElement
			: public LinkAdjacency
		{
			AssignableElementPtr linkTarget;
			int64_t childCount;
		private:
			GST::ClientUtils::MoMaPropertyValueListPtr assignedValues;
		public:
			LinkedElement()
				: linkTarget()
				, childCount(0)
			{

			};
			LinkedElement(const LinkedElement& other)
				: LinkAdjacency(other)
				, linkTarget(other.linkTarget)
				, childCount(0)
			{
			};
			LinkedElement(const LinkAdjacency& other)
				: LinkAdjacency(other)
				, linkTarget()
				, childCount(0)
			{
			};
			void addMoMapropertyValue(MoMaPropertyValuePtr _propvalue)
			{
				if(!assignedValues)
					assignedValues = boost::make_shared<MoMaPropertyValueList>();
				assignedValues->push_back(_propvalue);

			};

			void setLinkTarget(const AssignableElementPtr lt)
			{
				linkTarget = lt;
			}

			Element::ElementId getLinkedElementId() const
			{
				return linkTarget->getElement()->id;
			}

			LinkAdjacency::LinkId getNodelinkId() const
			{
				return id;
			}
		};


		typedef boost::shared_ptr<LinkedElement> LinkedElementPtr;
		typedef std::vector<LinkedElementPtr> LinkedElementList;
		typedef boost::shared_ptr<LinkedElementList> LinkedElementListPtr;

		struct LinkedFeatureDesc
			: public LinkAdjacency
		{
			AssignableFeatureDescPtr linkTarget;
		};
		typedef boost::shared_ptr<LinkedFeatureDesc> LinkedFeatureDescPtr;
		typedef std::vector<LinkedFeatureDescPtr> LinkedFeatureDescList;
		typedef boost::shared_ptr<LinkedFeatureDescList> LinkedFeatureDescListPtr;

		struct FeatureLinkIdGeometryIdPair
		{
			ClientUtils::LinkAdjacency::LinkId featureLinkId;
			long geometryId;

			FeatureLinkIdGeometryIdPair(ClientUtils::LinkAdjacency::LinkId featureLinkId, long geometryId)
				: featureLinkId(featureLinkId), geometryId(geometryId)
			{}
		};
		typedef std::vector<FeatureLinkIdGeometryIdPair> FeatureLinkIdGeometryIdPairList;
		typedef boost::shared_ptr<FeatureLinkIdGeometryIdPairList> FeatureLinkIdGeometryIdPairListPtr;

		/**
		 * Commit info
		 */
		struct GST_API_EXPORT Commit
		{
			typedef long CommitKey;

			CommitKey key;
			User::OwnerId userid;
			std::string message;
			///format YYYY-MM-DD"T"HH24:MI:SS, e.g. 2013-12-06T18:31:25 
			std::string timestamp;

			Commit(const long & key, const User::OwnerId & userid, const std::string & message, const std::string & timestamp)
				: key(key), userid(userid), message(message), timestamp(timestamp)
			{}
		};
		typedef boost::shared_ptr<Commit> CommitPtr;
		typedef std::vector<CommitPtr> CommitList;
		typedef boost::shared_ptr<CommitList> CommitListPtr;

		/**
		  * Detailed Commit info
		  */
		struct GST_API_EXPORT CommitVersion
		{
			typedef long VersionNo;

			VersionNo versionNo;
			Commit::CommitKey commitKey;
			long idgeo;

			CommitVersion(const VersionNo & versionNo, const Commit::CommitKey & commitKey, const long & idgeo)
				: versionNo(versionNo), commitKey(commitKey), idgeo(idgeo)
			{}
		};
		typedef boost::shared_ptr<CommitVersion> CommitVersionPtr;
		typedef std::vector<CommitVersionPtr>CommitVersionList;
		typedef boost::shared_ptr<CommitVersionList> CommitVersionListPtr;

	} //namespace ClientUtils

	//constants

	// sentinel to handle quirk in oracle (empty strings are interpreted as NULL (wtf))
	const std::string EMPTY_STRING_SENTINEL = "@@@empty_string@@@";
	// id for new colorvalue
	const ClientUtils::ColorValue::ColorValueId NEWCOLORVALUEID = -1;
	// id for new colorscale
	const ClientUtils::ColorScale::ColorScaleId NEWCOLORSCALEID = -1;
	// id of the root node link
	const ClientUtils::LinkAdjacency::LinkId ROOT_NODE_LINKID = 0;

	namespace ClientUtils {

		struct GST_API_EXPORT MoMaKey
		{
			LinkAdjacency::LinkId subtreeRootElement;
			LinkAdjacency::LinkId csRootElement;
			ColorScale::ColorScaleId csRootScale;

			MoMaKey( LinkAdjacency::LinkId subtreeRootElement = ROOT_NODE_LINKID, LinkAdjacency::LinkId csRootElement = ROOT_NODE_LINKID, ColorScale::ColorScaleId csRootScale = -1 ) 
				: subtreeRootElement(subtreeRootElement)
				, csRootElement(csRootElement)
				, csRootScale(csRootScale)
			{

			}
		};
		typedef boost::shared_ptr<MoMaKey> MoMaKeyPtr;

	} //namespace ClientUtils

	namespace exceptions
	{
		class LinkHasChildren
			: public GSTRuntimeException
		{
		public:

			ClientUtils::LinkAdjacencyListPtr childrenList;

			LinkHasChildren(const std::string & throwLocation, std::string msg, ClientUtils::LinkAdjacencyListPtr childs) 
				: GSTRuntimeException(throwLocation, msg)
				, childrenList(childs)
			{}
			// http://stackoverflow.com/questions/8618060/c-exception-subclass-string-member-variable
			virtual ~LinkHasChildren() throw() {}
		};

		class NodeStillLinkedTo
			: public GSTRuntimeException
		{
		public:

			ClientUtils::LinkAdjacencyListPtr nodeLinks;

			NodeStillLinkedTo(const std::string & throwLocation, std::string msg, ClientUtils::LinkAdjacencyListPtr links) 
				: GSTRuntimeException(throwLocation, msg)
				, nodeLinks(links)
			{}
			// http://stackoverflow.com/questions/8618060/c-exception-subclass-string-member-variable
			virtual ~NodeStillLinkedTo() throw() {}
		};
	} // namespace exception
} //namespace GST
#endif // GST_MoMaStructs_h__